﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text.RegularExpressions;

namespace RegexEditor
{
	/// <summary>
	/// A rich text box with syntax highlighting support for a regex.
	/// </summary>
	public class RegexHighlightTextBox : HighlightTextBox
	{
		#region Private Members

		private static readonly List<char> _openBraces = new List<char> { '(', '[', '{' };
		private static readonly List<char> _closeBraces = new List<char> { ')', ']', '}' };
		private static readonly List<Color> _braceColors = new List<Color> { Color.Navy, Color.SteelBlue, Color.DarkViolet };
		private List<RegexHighlight> _highlightPatterns = new List<RegexHighlight>();
		private List<Match> _highlightMatches = new List<Match>();
		private bool _replaceMode;
		private BraceMatch _braceMatch;

		#endregion

		#region Public Members

		/// <summary>
		/// Gets or sets whether the pattern is for a replacement, allowing substitutions instead of the regular syntax.
		/// </summary>
		[DefaultValue(false)]
		public bool ReplaceMode
		{
			get { return _replaceMode; }

			set
			{
				_replaceMode = value;
				_highlightPatterns.Clear();
				InitializeComponent();
			}
		}

		/// <summary>
		/// Performs syntax highlighting of a regex pattern.
		/// </summary>
		/// <param name="regex">The pattern representing the element to highlight.</param>
		/// <param name="foreColor">The foreground color.</param>
		/// <param name="backColor">The background color.</param>
		public virtual void Highlight(Regex regex, Color foreColor, Color backColor)
		{
			_highlightPatterns.Add(new RegexHighlight { Regex = regex, ForeColor = foreColor, BackColor = backColor });
		}

		#endregion

		#region Protected Members

		/// <summary>
		/// Performs syntax highlighting, executing all patterns and highlighting results.
		/// </summary>
		protected void Highlight()
		{
			RemoveFormatting();

			foreach (RegexHighlight highlight in _highlightPatterns)
				foreach (Match match in highlight.Regex.Matches(Text))
				{
					Highlight(match.Index, match.Length, highlight.ForeColor, highlight.BackColor);
					_highlightMatches.Add(match);
				}

			if (_braceMatch != null)
			{
				Highlight(_braceMatch.Index, 1, Color.White, _braceMatch.Color);
				Highlight(_braceMatch.Index + _braceMatch.Length - 1, 1, Color.White, _braceMatch.Color);
			}
		}

		/// <summary>
		/// Initializes the component, registering all syntax highlighting patterns.
		/// </summary>
		protected override void InitializeComponent()
		{
			base.InitializeComponent();

			if (!ReplaceMode)
			{
				// Line anchors
				Highlight(new Regex(@"[\^\$]"), Color.Black, Color.Yellow);

				// Character classes
				Highlight(new Regex(@"\[.*?\]"), Color.Blue, SystemColors.Window);
				Highlight(new Regex(@"\[\^.+?\]"), Color.Red, SystemColors.Window);
				Highlight(new Regex(@"\."), Color.Blue, SystemColors.Window);
				Highlight(new Regex(@"\\[wsd]"), Color.Blue, SystemColors.Window);
				Highlight(new Regex(@"\\[WSD]"), Color.Red, SystemColors.Window);
				Highlight(new Regex(@"\\p\{\w+\}"), Color.Blue, SystemColors.Window);
				Highlight(new Regex(@"\\P\{\w+\}"), Color.Red, SystemColors.Window);

				// Misc. anchors
				Highlight(new Regex(@"\\[AzZGbB]"), Color.Black, Color.Yellow);

				// Quantifiers
				Highlight(new Regex(@"[\+\?\*]"), Color.Magenta, SystemColors.Window);
				Highlight(new Regex(@"\{\d+(,(\d+)?)?\}"), Color.Magenta, SystemColors.Window);
				Highlight(new Regex(@"[\?\+\*]\?"), Color.White, Color.Magenta);
				Highlight(new Regex(@"\{\d+(,(\d+)?)?\}\?"), Color.White, Color.Magenta);

				// Groupings
				Highlight(new Regex(@"[\(\)\|]"), Color.Navy, SystemColors.Window);
				Highlight(new Regex(@"\?\<[^!=]+?\>"), Color.Green, SystemColors.Window);
				Highlight(new Regex(@"\(\?[imnsx]\:"), Color.Navy, SystemColors.Window);
				Highlight(new Regex(@"\(\?([:=!]|<=|<!|>)"), Color.Navy, SystemColors.Window);

				// Backreferences
				Highlight(new Regex(@"\\\d+"), Color.Navy, SystemColors.Window);
				Highlight(new Regex(@"\\k\<.+?\>"), Color.Green, SystemColors.Window);
			}
			else
			{
				// Substitutions
				Highlight(new Regex(@"\$\d+"), Color.Blue, SystemColors.Window);
				Highlight(new Regex(@"\$\{.+?\}"), Color.Green, SystemColors.Window);
				Highlight(new Regex(@"\$\$"), Color.FromArgb(0x99, 0x00, 0x00), SystemColors.Window);
				Highlight(new Regex(@"\$[&`'\+_]"), Color.Magenta, SystemColors.Window);
			}

			// Escapes
			Highlight(new Regex(@"\\[^wsdpWSDPkAzZGbB]"), Color.FromArgb(0x99, 0x00, 0x00), SystemColors.Window);
			Highlight(new Regex(@"\\(\d{3}|x\d{2}|c\w{1}|u\d{4})"), Color.FromArgb(0x99, 0x00, 0x00), SystemColors.Window);

			// Comments
			//Highlight(new Regex(@"[^\\]#.+$", RegexOptions.Multiline), Color.Gray, SystemColors.Window);
			Highlight(new Regex(@"\(\?#.+?\)"), Color.Gray, SystemColors.Window);
		}

		//protected override void OnSelectionChanged(EventArgs e)
		//{
		//	base.OnSelectionChanged(e);

		//	var selectionLock = NamedLock.AcquireLock("SelectionChanging", "RemovingFormatting", "Highlighting");

		//	if(selectionLock != null && SelectionLength == 0)
		//		using(selectionLock)
		//		{
		//			if (_openBraces.Contains(Text[SelectionStart]))
		//			{
		//				char openBrace = Text[SelectionStart];
		//				char closeBrace = _closeBraces[_openBraces.IndexOf(openBrace)];
		//				int counter = 1;

		//				for (int i = SelectionStart + 1; i < Text.Length; i++)
		//				{
		//					if (Text[i] == openBrace)
		//						counter++;
		//					else if (Text[i] == closeBrace)
		//						counter--;

		//					if (counter == 0)
		//					{
		//						_braceMatch = new BraceMatch
		//						{
		//							Index = SelectionStart,
		//							Length = i - SelectionStart + 1,
		//							Color = _braceColors[_openBraces.IndexOf(openBrace)]
		//						};
		//						Highlight();
		//						return;
		//					}
		//				}
		//			}

		//			if (SelectionStart < Text.Length && SelectionStart > 0 && _closeBraces.Contains(Text[SelectionStart - 1]))
		//			{
		//				char closeBrace = Text[SelectionStart - 1];
		//				char openBrace = _openBraces[_closeBraces.IndexOf(closeBrace)];

		//				int counter = 1;

		//				for (int i = SelectionStart - 2; i > -1; i--)
		//				{
		//					if (Text[i] == closeBrace)
		//						counter++;
		//					else if (Text[i] == openBrace)
		//						counter--;

		//					if (counter == 0)
		//					{
		//						_braceMatch = new BraceMatch
		//						{
		//							Index = i,
		//							Length = SelectionStart - i,
		//							Color = _braceColors[_closeBraces.IndexOf(closeBrace)]
		//						};

		//						Highlight();
		//						return;
		//					}
		//				}
		//			}

		//			if (_braceMatch != null)
		//			{
		//				_braceMatch = null;
		//				Highlight();
		//			}
		//		}
		//}

		/// <summary>
		/// Fires when text is changed, which triggers the highlighting process.
		/// </summary>
		/// <param name="e"></param>
		protected override void OnTextChanged(EventArgs e)
		{
			if (!NamedLock.IsLocked("RemovingFormatting") && !NamedLock.IsLocked("Highlighting"))
			{
				base.OnTextChanged(e);

				if (_highlightPatterns.Count > 0)
					Highlight();
			}
		}

		#endregion

		#region Nested Types

		private class RegexHighlight
		{
			public Regex Regex { get; set; }
			public Color ForeColor { get; set; }
			public Color BackColor { get; set; }
		}

		private class BraceMatch
		{
			public int Index { get; set; }
			public int Length { get; set; }
			public Color Color { get; set; }
		}

		#endregion
	}
}
